﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "CmdCreateViewPort.h"
#include "CommandIds.h"
#include "UiCommonDialog.h"
#include "CommandRegister.h"
#include "UiDocumentViewUtils.h"
#include "IUiView.h"
#include "IModelView.h"
#include "IViewClipRange.h"
#include "IDrawingViewport.h"
#include "IDrawingLayoutManager.h"
#include "IDrawingLayout.h"
#include "IUserTransaction.h"
#include "IPolyCurve.h"
#include "IPolygon.h"
#include "IReferencePlane.h"
#include "Vector3dUtils.h"
#include "IUiDocument.h"
#include "ModelViewUtils.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

#define VIEWPORT_NAME L"ViewPort"
#define POLY_VIEWPORT_NAME L"PolygonViewPort"
#define MULTI_VIEWPORT_NAME L"MultiViewPort"

using namespace gcmp;
using namespace Sample;

gcmp::IDrawingViewport* FindViewport(gcmp::IDrawingLayout* pDrawingLayout, const std::wstring& viewportName)
{
    if (pDrawingLayout == nullptr)
    {
        return nullptr;
    }

    gcmp::IDrawingViewport* pDrawingViewPort = nullptr;

    for (gcmp::IDrawingViewport* pDrawingViewPortItem : pDrawingLayout->GetAllViewports())
    {
        if (pDrawingViewPortItem->GetName() == viewportName)
        {
            pDrawingViewPort = pDrawingViewPortItem;
            break;
        }
    }
    return pDrawingViewPort;
}

std::vector<gcmp::ElementId> GetReferencePlaneIds(gcmp::IDocument* pDocument)
{
    std::vector<gcmp::ElementId> referencePlaneIds;
    if (nullptr != pDocument)
    {
        for each (IReferencePlane* pReferencePlane in IReferencePlane::GetAllReferencePlanes(pDocument))
        {
            referencePlaneIds.emplace_back(pReferencePlane->GetElementId());
        }
    }

    return referencePlaneIds;
}

gcmp::IModelView* GetOrCreateModelViewXY(gcmp::IDocument* pDocument, const std::wstring viewName/*L"ModelView2d"*/)
{
    gcmp::IModelView* pModelView2d = nullptr;
    //查找
    for each (gcmp::IModelView* pModelView in gcmp::IModelView::GetAllModelViews(pDocument))
    {
        if (gcmp::BuiltInViewType::TwoDimensional == pModelView->GetViewType())
        {
            if (gcmp::Vector3dUtils::IsEqual(Vector3d(1, 0, 0), pModelView->GetRightDirection()) && gcmp::Vector3dUtils::IsEqual(Vector3d(0, 1, 0), pModelView->GetUpDirection()))
            {
                if (pModelView->GetName() == viewName)
                {
                    pModelView2d = pModelView;
                    break;
                }
            }
        }
    }

    //创建
    if (nullptr == pModelView2d)
    {
        if (gcmp::IUserTransaction::IsInUserTransaction(pDocument, nullptr))
        {
            return gcmp::IModelView::Create(pDocument, viewName, gcmp::BuiltInViewType::TwoDimensional, gcmp::Vector3d(0, 0, -1), gcmp::Vector3d(0, 0, 0), gcmp::Vector3d(1, 0, 0), gcmp::Vector3d(0, 1, 0), gcmp::ElementCreationOptions::Normal);
        }

        OwnerPtr<gcmp::IUserTransaction> opUserTransaction = gcmp::IUserTransaction::Create(pDocument, L"创建2D模型视图");
        pModelView2d = gcmp::IModelView::Create(pDocument, viewName, gcmp::BuiltInViewType::TwoDimensional,
            gcmp::Vector3d(0, 0, -1), gcmp::Vector3d::Zero, gcmp::Vector3d(1, 0, 0), gcmp::Vector3d(0, 1, 0), gcmp::ElementCreationOptions::Normal);
        if (nullptr == pModelView2d)
        {
            opUserTransaction->Rollback();
            return nullptr;
        }
        pModelView2d->SetViewScale(0.100000);
        pModelView2d->SetDetailLevel(ModelViewDetailLevel::Fine);
        for (gcmp::ElementId elementId : GetReferencePlaneIds(pDocument))
        {
            pModelView2d->SetElementVisibilityByElementId(elementId, false);
        }
        opUserTransaction->Commit();
    }
    return pModelView2d;
}

REGISTER_COMMAND(CreateViewPortCmd)

CreateViewPortCmd::CreateViewPortCmd()
    :CommandBase(Sample::ID_CMD_CREATE_VIEWPORT)
{}

bool CreateViewPortCmd::IsVisible() const
{
    return UiDocumentViewUtils::GetCurrentDocument() != nullptr;
}

bool CreateViewPortCmd::IsEnabled() const
{
    gcmp::IDocument* pCurrentDocument = gcmp::UiDocumentViewUtils::GetCurrentDocument();
    if (pCurrentDocument == nullptr)
    {
        return false;
    }

    gcmp::IUiView* pUiView = gcmp::UiDocumentViewUtils::GetCurrentUiView();
    if (nullptr != pUiView)
    {
        if (pUiView->IsSubUiView())
        {
            std::wstring uiViewTag = pUiView->GetTag();
            if (uiViewTag == L"_1")
            {
                return true;
            }
        }
    }

    gcmp::IModelView* pCurrentModelView = gcmp::UiDocumentViewUtils::GetCurrentModelView();
    if (pCurrentModelView == nullptr)
    {
        return false;
    }

    if (pCurrentModelView->GetViewType() == gcmp::BuiltInViewType::Drawing)
    {
        return true;
    }

    return false;
}

Vector3d CreateViewPortCmd::GetPointInDrawing(const IModelView* pModelView, int index, double dLength, double dWidth)
{
    double dScale = pModelView->GetViewScale();
    Vector3d pointOright(0, 0, 0);
    dLength *= dScale;
    return pointOright - Vector3d(dLength, 0, 0);
}

gcmp::OwnerPtr<gcmp::IAction> CreateViewPortCmd::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
{
    // 本命令基于既有的图纸视图进行视口创建，在执行本命令时请先手动创建一个图纸视图，并确保图纸视图名称为：ModelViewForDrawing
    gcmp::IDrawingLayoutManager* pDrawingLayoutManager = gcmp::IDrawingLayoutManager::Get(gcmp::UiDocumentViewUtils::GetCurrentDocument());
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDrawingLayoutManager, L"pDrawingLayoutManager不能为空", L"GDMP", L"2023-09-30");
    std::vector<IDrawingLayout*> allLayout = pDrawingLayoutManager->GetAllLayouts();
    if (allLayout.empty())
    {
        return nullptr;
    }
    gcmp::IDrawingLayout* pDrawingLayout = nullptr;
    for (gcmp::IDrawingLayout* pDrawingLayoutItem : allLayout)
    {
        // 在视图名称为"ModelViewForDrawing"图纸视图进行视口创建
        if (pDrawingLayoutItem->GetName() == L"ModelViewForDrawing")
        {
            pDrawingLayout = pDrawingLayoutItem;
            break;
        }
    }
    if (nullptr == pDrawingLayout)
    {
        UiCommonDialog::ShowMessageBox(L"创建视口失败", L"请先手动创建一个图纸视图，并确保图纸名为：ModelViewForDrawing", (int)UiCommonDialog::ButtonType::Yes);
        return nullptr;
    }
    gcmp::IDocument* pDocument = gcmp::UiDocumentViewUtils::GetCurrentDocument();
    OwnerPtr<IUserTransaction> opUserTransaction = IUserTransaction::Create(pDocument, L"CreateViewPort", true);
    gcmp::IModelView* pModelView = GetOrCreateModelViewXY(pDocument, L"ForViewPort");
    pModelView->SetUserDataId(PlanViewType);
    if (nullptr == pModelView)
    {
        opUserTransaction->Rollback();
        return nullptr;
    }

    //
    // 1-创建一个简单的矩形视口
    gcmp::IDrawingViewport* pDrawingViewPort = FindViewport(pDrawingLayout, VIEWPORT_NAME);
    if (pDrawingViewPort == nullptr)
    {
        std::wstring errorMessage = L"";
        pDrawingViewPort = pDrawingLayout->CreateViewport(pModelView->GetElementId(), VIEWPORT_NAME, Vector3d(10, 10, 10), &errorMessage);
        if (pDrawingViewPort == nullptr)
        {
            opUserTransaction->Rollback();
            UiCommonDialog::ShowMessageBox(L"创建视口失败", errorMessage, (int)UiCommonDialog::ButtonType::Yes);
            return nullptr;
        }
    }
    //
    //  2-创建多边形视口
    gcmp::IDrawingViewport* pDrawingViewPortPolygon = FindViewport(pDrawingLayout, POLY_VIEWPORT_NAME);
    if (pDrawingViewPortPolygon == nullptr)
    {
        double edgeLength = 5000;
        std::vector<gcmp::Vector2d> points2d = { gcmp::Vector2d(edgeLength / 2,-edgeLength * pow(3,0.5) / 2)
            , gcmp::Vector2d(edgeLength,0)
            , gcmp::Vector2d(edgeLength / 2,edgeLength * pow(3,0.5) / 2)
            , gcmp::Vector2d(-edgeLength / 2,edgeLength * pow(3,0.5) / 2)
            , gcmp::Vector2d(-edgeLength,0)
            , gcmp::Vector2d(-edgeLength / 2,-edgeLength * pow(3,0.5) / 2)
            , gcmp::Vector2d(edgeLength / 2,-edgeLength * pow(3,0.5) / 2) };
        OwnerPtr<gcmp::IPolyCurve> opPolyCurve = gcmp::IPolyCurve::Create(points2d);
        OwnerPtr<gcmp::IPolygon> opPolygon = gcmp::IPolygon::Create();
        opPolygon->AddPolyCurveByClone(opPolyCurve.get());
        if (!opPolygon->IsRegularized())
        {
            return nullptr;
        }
        pDrawingViewPortPolygon = pDrawingLayout->CreatePolygonViewport(pModelView->GetElementId(), POLY_VIEWPORT_NAME, 
            TransferOwnership(opPolygon), Vector3d(-edgeLength / 10, -edgeLength / 10, 10), nullptr, true);
        if (pDrawingViewPortPolygon == nullptr)
        {
            opUserTransaction->Rollback();
            UiCommonDialog::ShowMessageBox(GBMP_TR(L"创建多边形视口失败"), GBMP_TR(L"创建多边形视口失败"), (int)(UiCommonDialog::ButtonType::OK));
            return nullptr;
        }
        pDrawingViewPortPolygon->SetName(POLY_VIEWPORT_NAME);
    }
    //
    // 3-创建多视口
    gcmp::IDrawingViewport* pDrawingViewMulti = FindViewport(pDrawingLayout, MULTI_VIEWPORT_NAME);
    if (pDrawingViewMulti == nullptr)
    {
        Vector3d firstPt{ 0, 10000, 0 };
        Vector3d secondPt{ 0, -10000, 0 };
        Coordinate3d coord(pModelView->GetOrigin(), pModelView->GetRightDirection(), pModelView->GetUpDirection());
        auto funCallback = std::bind(&CreateViewPortCmd::GetPointInDrawing, this,
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
        int index = 1;
        gcmp::ElementId modelViewId = pModelView->GetElementId();
        pDrawingViewMulti = pDrawingLayout->CreateMultiViewport(modelViewId, MULTI_VIEWPORT_NAME, coord, firstPt, secondPt, 10000.0, index, funCallback, true);
        if (pDrawingViewMulti == nullptr)
        {
            opUserTransaction->Rollback();
            UiCommonDialog::ShowMessageBox(GBMP_TR(L"创建多视口失败"), GBMP_TR(L"创建多视口失败"), (int)(UiCommonDialog::ButtonType::OK));
            return nullptr;
        }
        pDrawingViewMulti->SetName(MULTI_VIEWPORT_NAME);
    }
#pragma endregion
    //active viewport
    IViewClipRange* pViewClipRange = quick_cast<IViewClipRange>(pDocument->GetElement(pModelView->GetViewClipRangeId()));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(nullptr != pViewClipRange, L"Null pViewClipRange!", L"GDMPLab", L"2024-08-29");
    pViewClipRange->SetClipVisible(true);
    if (pDrawingViewPort)
    {
        pDrawingViewPort->SetFrameVisibility(GraphicsNodeVisibility::Always);
        pDrawingViewPort->SetIsEnableActive(true);
        pDrawingViewPort->Activate();
    }
    if (pDrawingViewPortPolygon)
    {
        pDrawingViewPortPolygon->SetFrameVisibility(GraphicsNodeVisibility::Always);
        pDrawingViewPortPolygon->SetIsEnableActive(true);
        pDrawingViewPortPolygon->Activate();
    }
    if (pDrawingViewMulti)
    {
        pDrawingViewMulti->SetFrameVisibility(GraphicsNodeVisibility::Always);
        pDrawingViewMulti->SetIsEnableActive(true);
        pDrawingViewMulti->Activate();
    }

    opUserTransaction->Commit();
    UiDocumentViewUtils::GetCurrentUiDocument()->UpdateView();
    return nullptr;
}